package com.wxd.woff.table.name;

import com.wxd.woff.AbstractOTFDataStream;
import com.wxd.woff.AbstractWoffTable;
import com.wxd.woff.ByteBufferOTFDataStream;
import com.wxd.woff.WoffFont;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * name table
 * Created with IntelliJ IDEA.
 *
 * @author: wangxindong
 * @date: 2018/9/2-07:19
 */
public class NamingTable extends AbstractWoffTable {

    public static final String TAG = "name";

    private List<NameRecord> nameRecords;

    private Map<Integer, Map<Integer, Map<Integer, Map<Integer, String>>>> lookupTable;

    private String fontFamily = null;
    private String fontSubFamily = null;
    private String psName = null;

    public NamingTable(WoffFont woffFont) {
        super(woffFont);
    }

    @Override
    public void read(WoffFont woffFont, AbstractOTFDataStream stream) throws IOException {
        stream.seek(offset);
        byte[] fontData = stream.read(compLength);
        byte[] inflateFont = inflateFontData(fontData);
        AbstractOTFDataStream data = new ByteBufferOTFDataStream(inflateFont);

        int format = data.readUnsignedShort();
        int count = data.readUnsignedShort();
        int stringOffset = data.readUnsignedShort();
        nameRecords = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            NameRecord nr = new NameRecord();
            nr.initData(woffFont, data);
            nameRecords.add(nr);
        }

        for (NameRecord nr : nameRecords) {
            if (nr.getOffset() > compLength) {
                nr.setString(null);
                continue;
            }

            // 跳到指定的offset
            // 2 * 3 是前面固定的6个字节
            // 2 * 6 是每一个nameRecords所占的字节数
            data.seek(nr.getOffset() + (2 * 3) + (2 * 6) * count);

            int platform = nr.getPlatformId();
            int encoding = nr.getEncodingID();

            Charset charset = StandardCharsets.ISO_8859_1;
            if (platform == NameRecord.PLATFORM_WINDOWS &&
                    (encoding == NameRecord.ENCODING_WINDOWS_SYMBOL ||
                            encoding == NameRecord.ENCODING_WINDOWS_UNICODE_BMP)) {
                charset = StandardCharsets.UTF_16BE;
            } else if (platform == NameRecord.PLATFORM_UNICODE) {
                charset = StandardCharsets.UTF_16BE;
            } else if (platform == NameRecord.PLATFORM_ISO) {
                switch (encoding) {
                    case 0:
                        charset = StandardCharsets.US_ASCII;
                        break;
                    case 1:
                        // iso 10646 not font
                        charset = StandardCharsets.ISO_8859_1;
                        break;
                    case 2:
                        charset = StandardCharsets.ISO_8859_1;
                        break;
                    default:
                        break;
                }
            }
            String string = data.readString(nr.getLength(), charset);
            nr.setString(string);
        }

        lookupTable = new HashMap<>(nameRecords.size());
        for (NameRecord nr : nameRecords) {
            // name id
            Map<Integer, Map<Integer, Map<Integer, String>>> platformLookup = lookupTable.get(nr.getNameId());
            if (platformLookup == null) {
                platformLookup = new HashMap<>();
                lookupTable.put(nr.getNameId(), platformLookup);
            }
            // platform id
            Map<Integer, Map<Integer, String>> encodingLookup = platformLookup.get(nr.getPlatformId());
            if (encodingLookup == null) {
                encodingLookup = new HashMap<>();
                platformLookup.put(nr.getPlatformId(), encodingLookup);
            }
            // encoding id
            Map<Integer, String> languageLookup = encodingLookup.get(nr.getEncodingID());
            if (languageLookup == null) {
                languageLookup = new HashMap<>();
                encodingLookup.put(nr.getEncodingID(), languageLookup);
            }
            // language id / string
            languageLookup.put(nr.getLanguageId(), nr.getString());
        }

        // extract strings of interest
        fontFamily = getEnglishName(NameRecord.NAME_FONT_FAMILY_NAME);
        fontSubFamily = getEnglishName(NameRecord.NAME_FONT_SUB_FAMILY_NAME);

        // extract PostScript name, only these two formats are valid
        psName = getName(NameRecord.NAME_POSTSCRIPT_NAME,
                NameRecord.PLATFORM_MACINTOSH,
                NameRecord.ENCODING_MACINTOSH_ROMAN,
                NameRecord.LANGUGAE_MACINTOSH_ENGLISH);
        if (psName == null) {
            psName = getName(NameRecord.NAME_POSTSCRIPT_NAME,
                    NameRecord.PLATFORM_WINDOWS,
                    NameRecord.ENCODING_WINDOWS_UNICODE_BMP,
                    NameRecord.LANGUGAE_WINDOWS_EN_US);
        }
        if (psName != null) {
            psName = psName.trim();
        }
    }


    /**
     * Helper to get English names by best effort.
     */
    private String getEnglishName(int nameId) {
        // Unicode, Full, BMP, 1.1, 1.0
        for (int i = 4; i >= 0; i--) {
            String nameUni =
                    getName(nameId,
                            NameRecord.PLATFORM_UNICODE,
                            i,
                            NameRecord.LANGUGAE_UNICODE);
            if (nameUni != null) {
                return nameUni;
            }
        }

        // Windows, Unicode BMP, EN-US
        String nameWin =
                getName(nameId,
                        NameRecord.PLATFORM_WINDOWS,
                        NameRecord.ENCODING_WINDOWS_UNICODE_BMP,
                        NameRecord.LANGUGAE_WINDOWS_EN_US);
        if (nameWin != null) {
            return nameWin;
        }

        // Macintosh, Roman, English
        String nameMac =
                getName(nameId,
                        NameRecord.PLATFORM_MACINTOSH,
                        NameRecord.ENCODING_MACINTOSH_ROMAN,
                        NameRecord.LANGUGAE_MACINTOSH_ENGLISH);
        if (nameMac != null) {
            return nameMac;
        }

        return null;
    }

    /**
     * Returns a name from the table, or null it it does not exist.
     *
     * @param nameId     Name ID from NameRecord constants.
     * @param platformId Platform ID from NameRecord constants.
     * @param encodingId Platform Encoding ID from NameRecord constants.
     * @param languageId Language ID from NameRecord constants.
     * @return name, or null
     */
    public String getName(int nameId, int platformId, int encodingId, int languageId) {
        Map<Integer, Map<Integer, Map<Integer, String>>> platforms = lookupTable.get(nameId);
        if (platforms == null) {
            return null;
        }
        Map<Integer, Map<Integer, String>> encodings = platforms.get(platformId);
        if (encodings == null) {
            return null;
        }
        Map<Integer, String> languages = encodings.get(encodingId);
        if (languages == null) {
            return null;
        }
        return languages.get(languageId);
    }
    
    public String getPostScriptName(){
        return psName;
    }

    @Override
    public String toString() {
        return "AbstractWoffTable{" +
                "tag='" + tag + '\'' +
                ", offset=" + offset +
                ", compLength=" + compLength +
                ", origLength=" + origLength +
                ", origChecksum=" + origChecksum +
                ", nameRecords=" + nameRecords +
                ", lookupTable=" + lookupTable +
                ", fontFamily='" + fontFamily + '\'' +
                ", fontSubFamily='" + fontSubFamily + '\'' +
                ", psName='" + psName + '\'' +
                '}';
    }
}
